from IPython.display import HTML, display, Markdown
from bokeh.layouts import column, row
from bokeh.models import LinearAxis, Range1d, NumeralTickFormatter,DatetimeTickFormatter, HoverTool
from bokeh.plotting import figure, output_notebook, show
from bokeh.resources import INLINE
import datetime
import dateutil
import glob
import h5py
import numpy as np
import pandas as pd
import pytz
import sys
from plotting import (plot_candlestick_singleday,
plot_trades_singleday,
plot_volume_singleday,
pnl_multilineplot_bokeh,
pnl_bar_win_lose_rect,
pnl_bar_long_short_rect,
durationplot)
output_notebook()
eastern_tz = pytz.timezone('US/Eastern')
local_tz = dateutil.tz.tzlocal()
def dt_from_muts(ts):
"""
Convert an integer number of microseconds since the epoch into a datetime object
(implicitly in Eastern time, but no timezone set)
"""
dt = datetime.datetime.fromtimestamp(int(ts // 1000000)).replace(microsecond=int(ts % 1000000),
tzinfo=local_tz).astimezone(eastern_tz).replace(
tzinfo=None)
return dt
def top_bottom_minbar(odr, annotate=False, resample_freq='15min' ,top_bot='all', num_charts=10 ):
"""
generates minute bar plots for Top/Bottom 10 orders a user made
"""
j = []
# Create GroupBys of Top 10/ Bottom 10 Trades in Strategy / Export date+symbol to list
odr_p = odr[odr.entry_pl>0]
odr_n = odr[odr.entry_pl<0]
if odr_p.shape[0] == 0 and top_bot=='top':
print 'No profitable trades in strategy'
return
if odr_n.shape[0] == 0 and top_bot=='bot':
print 'No losing trades in strategy'
return
if top_bot == 'top' and odr_p.shape[0] > 0:
j.extend(pd.DataFrame(odr_p.entry_pl.groupby([odr_p.index.date.astype(str),odr_p.symbol]).sum()).sort_values('entry_pl',ascending=False).iloc[:num_charts].index)
elif top_bot == 'bot'and odr_n.shape[0] > 0:
j.extend(pd.DataFrame(odr_n.entry_pl.groupby([odr_n.index.date.astype(str),odr_n.symbol]).sum()).sort_values('entry_pl',ascending=True).iloc[:num_charts].index)
MIN_BAR_DIR = '/mnt/gv0/mktdb5/bar/time/60/'
CQ_MIN_BAR_DIR = "/srv/mktdb5/bar/time/60/"
plt_list = []
for i in j:
h5 = CQ_MIN_BAR_DIR+i[0]+'.hdf5' ## will need to replace with wes's MB read
sym = i[1]
dataset = 'groups/bar/'+sym
f = h5py.File(h5, 'r')
df = pd.DataFrame(f[dataset][:])
if resample_freq == '1min' :
bar_width = 50000
else:
bar_width = max(500000, 25000000/(df.shape[0]*1.25))
df.index = df.timestamp.apply(lambda x: dt_from_muts(x))
candle_plot = plot_candlestick_singleday(df,
symbol=sym,
resample_freq=resample_freq,
annotate=annotate,
bar_width=bar_width)
trd = odr[(odr.symbol == sym) & (odr.index.date.astype(str)== i[0])] # filter trade data to this specific trade
candle_plot = plot_trades_singleday(trd, figure=candle_plot) # add trades to candlestick plot
vol_plot = plot_volume_singleday(df, symbol=sym, annotate=annotate)
plt_list.append(column(candle_plot,vol_plot))
return plt_list
def summaryWinLoseDate(df):
"""
returns aggregation of trade data winners/ losers and total by date for multiple statistics
"""
grouper=df[['entry_pl','entry_shares','entry_time','Win']].groupby([df.index.date,df.Win])
jg=grouper.aggregate({'entry_pl':'sum','Win':'count','entry_shares':'sum'}).unstack()
jg=jg.swaplevel(0,1,axis=1).sort_index(axis=1)
tp=pd.DataFrame()
tl=pd.DataFrame()
if 'Win' in jg.columns:
tp=jg['Win'].copy()
tp['AvgPNL']=tp.entry_pl/tp.Win
if 'Loss' in jg.columns:
tl=jg['Loss'].copy()
tl['AvgPNL']=tl.entry_pl/tl.Win
grouper=df[['entry_pl','entry_shares','entry_time','Win']].groupby([df.index.date])
pb=pd.DataFrame(grouper.aggregate({'entry_pl':'sum','Win':'count','entry_shares':'sum'}))
pb['AvgPNL']=pb.entry_pl/pb.Win
if tp.shape[0] and tl.shape[0] > 0:
summ=pd.concat({'Total':pb,'Winners':tp,'Losers':tl},axis=1)
summ=summ.applymap(lambda x: round(x,2))
summ=summ[['Total','Winners','Losers']]
elif tp.shape[0] >0 :
summ=pd.concat({'Total':pb,'Winners':tp},axis=1)
summ=summ.applymap(lambda x: round(x,2))
summ=summ[['Total','Winners']]
else:
summ=pd.concat({'Total':pb,'Losers':tl},axis=1)
summ=summ.applymap(lambda x: round(x,2))
summ=summ[['Total','Losers']]
summ.fillna(0,inplace=True)
return summ
# Read and prepare order trades data
non_null_columns = ['entry_pl', 'entry_price', 'entry_shares', 'entry_side', 'entry_time', 'exit_price',
'exit_shares', 'exit_time', 'exit_side']
odr = pd.read_csv('trades.csv')
odr.index = pd.to_datetime(odr.entry_time)
odr.sort_index(inplace=True)
odr.entry_time = pd.to_datetime(odr.entry_time)
odr.exit_time = pd.to_datetime(odr.exit_time)
odr['returns'] = odr.entry_pl / (odr.entry_price*odr.entry_shares)
odr['duration'] = (odr.exit_time-odr.entry_time).astype('timedelta64[m]')
odr['Win'] = (odr.entry_pl> 0).map({True:'Win', False: 'Loss' })
odr['entry_pl_share'] = odr.entry_pl/odr.entry_shares.abs()
odr['Efficiency'] = odr.entry_pl/odr.entry_shares.abs()
no_null = (~odr[non_null_columns].isnull().any(axis=1))
odr = odr.loc[no_null]
display(Markdown('# Strategy Summary Notebook \n \
This is the summary report of your cloudquant strategy, in it you will find high level statistics and visualizations of strategy performance and trade analysis.\
'))
# generate formated strings for table output
entry_date = str(odr.index.date.min())
exit_date = str(odr.index.date.max())
tpnl = format(odr.entry_pl.sum(), '.2f')
avef = format(odr.Efficiency.mean(), '.2f')
winp = format(100* float(odr[odr.entry_pl>0].entry_pl.count())/ odr.entry_pl.count(), '.2f')
tloss = format(odr[odr.Win=='Loss'].entry_pl.sum(), '.2f')
lossp = format(100* abs(odr[odr.Win=='Loss'].entry_pl.sum()) / (odr.entry_pl.sum()), '.2f')
# Html formated summary notes
HTML('<b>Entry Date :</b>  '+entry_date+'<br>\
<b>Exit Date :</b>  '+exit_date+'<br>\
<table style="width:35%">\
<tr><td style="background-color:#3090C7;"><b>Total PNL</b></td><td>'+tpnl+'</td></tr>\
<tr><td style="background-color:#3090C7;"><b>Avg Efficiency</b></td><td>'+avef+'</td></tr>\
<tr><td style="background-color:#3090C7;"><b>Win Percent</b></td><td>'+winp+'%'+'</td></tr>\
<tr><td style="background-color:#3090C7;"><b>Total Losses</b></td><td>'+tloss+'</td></tr>\
<tr><td style="background-color:#3090C7;"><b>Loss/PNL</b></td><td>'+lossp+'%'+'</td></tr>\
</table>')
show(pnl_multilineplot_bokeh(odr))
from plotting import pnl_bar_long_short_rect
pb = pd.DataFrame(odr.entry_pl.groupby([odr.index.year,odr.index.month,odr.Win]).sum()).reset_index(level=2)
pb.index = pb.index.map(lambda x: pd.to_datetime('{}/{}'.format(str(x[0]),str(x[1]))))
ymin,ymax = min(pb['entry_pl']), max(pb['entry_pl'])
tot=pd.DataFrame(pb.entry_pl.groupby(pb.index).sum())
tot['Win']='Total'
pb=pb.append(tot)
pg = pd.DataFrame(odr.entry_pl.groupby([odr.index.year,odr.index.month,odr.entry_side]).sum()).reset_index(level=2)
pg.index = pg.index.map(lambda x: pd.to_datetime('{}/{}'.format(str(x[0]),str(x[1]))))
pg.entry_side=pg.entry_side.map({-1:'Short',1:'Long'})
if ~pb.empty:
p = pnl_bar_win_lose_rect(pb, ymin,ymax)
if ~pg.empty:
p1 = pnl_bar_long_short_rect(pg, ymin, ymax)
if p and p1:
show(row(p,p1))
BS= (odr.entry_side==1) & (odr.duration<=1440) & np.isfinite(odr.returns)
SS= (odr.entry_side==0) & (odr.duration<=1440) & np.isfinite(odr.returns)
if odr[BS].shape[0]>0:
show(durationplot(odr[BS], title='Long Position Returns by Duration in Minutes'))
if odr[SS].shape[0]>0:
show(durationplot(odr[SS], title='Short Position Returns by Duration in Minutes'))
odr.sort_values(by='entry_pl',ascending=False)[['entry_pl','symbol','entry_shares','entry_price','exit_price','Efficiency']].iloc[:10]
top_10=top_bottom_minbar(odr,
annotate=True,
resample_freq='1min',
top_bot='top',
num_charts=10)
if top_10:
show ( column (*top_10))
odr.sort_values(by='entry_pl',ascending=True)[['entry_pl','symbol','entry_shares','entry_price','exit_price','Efficiency']].iloc[:10]
bot_10=top_bottom_minbar(odr,
annotate=True,
resample_freq='1min',
top_bot='bot',
num_charts=10)
if bot_10:
show ( column (*bot_10))
odr.sort_values(by='Efficiency',ascending=False)[['entry_pl','symbol','entry_shares','entry_price','exit_price','Efficiency']].iloc[:10]
odr.sort_values(by='Efficiency',ascending=True)[['entry_pl','symbol','entry_shares','entry_price','exit_price','Efficiency']].iloc[:10]
summaryWinLoseDate(odr).fillna(0).style.bar(subset='Total',color='#d65f5f')
# Report View
def report_view():
'''Returns an IPython.display.HTML object that hides input cells, output prompts, and the
Bokeh banner to make a notebook look like a report.
To use this function, just call report_view() in a cell all by itself in your notebook.
'''
return HTML('''<script>
code_show=true;
function code_toggle() {
if (code_show){
$('div.input').hide();
$('div.bk-banner').hide();
$('div.output_prompt').css({"visibility":"hidden"});
} else {
$('div.input').show();
$('div.bk-banner').show();
$('div.output_prompt').css({"visibility":"visible"});
}
code_show = !code_show
}
$( document ).ready(code_toggle);
</script>
<form action="javascript:code_toggle()"><input type="submit" value="Click here to toggle on/off the raw code."></form>''')
report_view()